{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import pickle\n",
    "from collections import defaultdict\n",
    "import re\n",
    "from bs4 import BeautifulSoup\n",
    "import sys\n",
    "import os\n",
    "os.environ['KERAS_BACKEND']='theano'\n",
    "from keras.preprocessing.text import Tokenizer\n",
    "from keras.preprocessing.sequence import pad_sequences\n",
    "from keras.utils.np_utils import to_categorical\n",
    "from keras.layers import Embedding\n",
    "from keras.layers import Dense, Input, Flatten\n",
    "from keras.layers import Conv1D, MaxPooling1D, Embedding, Dropout, LSTM, GRU, Bidirectional\n",
    "from keras.models import Model\n",
    "from keras.callbacks import ModelCheckpoint\n",
    "import matplotlib.pyplot as plt\n",
    "plt.switch_backend('agg')\n",
    "from keras import backend as K\n",
    "from keras.engine.topology import Layer, InputSpec\n",
    "from keras import initializers\n",
    "%matplotlib inline\n",
    "\n",
    "import tensorflow as tf\n",
    "\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from tensorflow.keras import Input, Model\n",
    "from tensorflow.keras.layers import Embedding, Dense, Bidirectional, GRU, LSTM, TimeDistributed\n",
    "\n",
    "from attention import Attention"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 处理数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train len 2064\n",
      "test len 688\n",
      "word2id len 561\n"
     ]
    }
   ],
   "source": [
    "fname_train = '../data/train-HAN.pkl'\n",
    "fname_test = '../data/test-HAN.pkl'\n",
    "\n",
    "with open(fname_train, 'rb') as inp:\n",
    "    word2id = pickle.load(inp)\n",
    "    id2word = pickle.load(inp)\n",
    "    relation2id = pickle.load(inp)\n",
    "    entity2id = pickle.load(inp)\n",
    "    train = pickle.load(inp)\n",
    "    labels = pickle.load(inp)\n",
    "    position1 = pickle.load(inp)\n",
    "    position2 = pickle.load(inp)\n",
    "    E = pickle.load(inp)\n",
    "\n",
    "with open(fname_test, 'rb') as inp:\n",
    "    test = pickle.load(inp)\n",
    "    labels_t = pickle.load(inp)\n",
    "    position1_t = pickle.load(inp)\n",
    "    position2_t = pickle.load(inp)\n",
    "    E_t = pickle.load(inp)\n",
    "\n",
    "print(\"train len\", len(train))\n",
    "print(\"test len\", len(test))\n",
    "print(\"word2id len\", len(word2id))\n",
    "\n",
    "\n",
    "labels = np.array(pd.get_dummies(labels))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "class HAN(Model):\n",
    "    def __init__(self,\n",
    "                 MAX_WORD_NUM = 20,\n",
    "                 MAX_SEN_NUM = 20,\n",
    "                 EMBEDDING_DIM = 200,\n",
    "                 class_num = 6,\n",
    "                 last_activation='softmax'):\n",
    "        super(HAN, self).__init__()\n",
    "        self.MAX_WORD_NUM = MAX_WORD_NUM\n",
    "        self.MAX_SEN_NUM = MAX_SEN_NUM\n",
    "        self.EMBEDDING_DIM = EMBEDDING_DIM\n",
    "        self.class_num = class_num\n",
    "        self.last_activation = last_activation\n",
    "\n",
    "        \n",
    "        \n",
    "        sequence_input = Input(shape=(self.MAX_WORD_NUM,), dtype='int32')\n",
    "        pos1_input = Input(shape=(self.MAX_SEN_NUM,self.MAX_WORD_NUM,), dtype='int32')\n",
    "        pos2_input = Input(shape=(self.MAX_SEN_NUM,self.MAX_WORD_NUM,), dtype='int32')\n",
    "        e_input = Input(shape=(self.MAX_SEN_NUM,self.MAX_WORD_NUM,), dtype='int32')\n",
    "\n",
    "        embedding_matrix = np.random.random((len(word2id) + 1, self.EMBEDDING_DIM))\n",
    "        for word, i in word2id.items():\n",
    "            embedding_vector = word2id[word]\n",
    "            if embedding_vector is not None:\n",
    "                # words not found in embedding index will be all-zeros.\n",
    "                embedding_matrix[i] = embedding_vector\n",
    "                \n",
    "        word_embeds = Embedding(len(word2id) + 1,\n",
    "                            self.EMBEDDING_DIM,\n",
    "                            weights=[embedding_matrix],\n",
    "                            input_length = self.MAX_WORD_NUM,\n",
    "                            trainable=True)        \n",
    "        # 文档特征##################################################\n",
    "        # Word part##############\n",
    "        word_embedding = word_embeds(sequence_input)\n",
    "        #print(\"word_embedding:\",word_embedding.shape)##\n",
    "        \n",
    "        word_embedding = Bidirectional(LSTM(64, return_sequences=True))(word_embedding)\n",
    "        #print(\"word_embedding:\",word_embedding.shape)##\n",
    "        word_emb = Attention(self.MAX_WORD_NUM)(word_embedding)\n",
    "        #print(\"word_embedding:\",word_emb.shape)##\n",
    "        wordEncoder = Model(sequence_input, word_emb)\n",
    "        # Sentence part\n",
    "        self.sen_encoder = TimeDistributed(wordEncoder)#(sentence_input)\n",
    "        self.sen_embedding = Bidirectional(LSTM(128, return_sequences=True))#(sen_encoder)\n",
    "        self.sen_emb = Attention(self.MAX_SEN_NUM)#(sen_embedding)##\n",
    "        \n",
    "        \n",
    "        # pos1\n",
    "        #print(\"pos1_input\",pos1_input.shape)##\n",
    "        self.pos1_f = Bidirectional(LSTM(64, return_sequences=True))#(pos1_input)\n",
    "        self.pos1_emb = Attention(self.MAX_SEN_NUM)#(pos1)\n",
    "        \n",
    "        \n",
    "        # pos2\n",
    "        #print(\"pos2_input\",pos2_input.shape)##\n",
    "        self.pos2_f = Bidirectional(LSTM(64, return_sequences=True))#(pos1_input)\n",
    "        self.pos2_emb = Attention(self.MAX_SEN_NUM)#(pos1)\n",
    "       \n",
    "\n",
    "        #e\n",
    "        #print(\"e_input\",e_input.shape)##\n",
    "        self.e_f = Bidirectional(LSTM(64, return_sequences=True))#(pos1_input)\n",
    "        self.e_emb = Attention(self.MAX_SEN_NUM)#(pos1)\n",
    "       \n",
    "\n",
    "        self.feature = Dense(64)#(feature)\n",
    "\n",
    "        self.preds = Dense(class_num, activation=self.last_activation)#(feature)\n",
    "\n",
    "    def call(self, inputs):\n",
    "\n",
    "        x_sentence = self.sen_encoder(inputs[0])\n",
    "        x_sentence = self.sen_embedding(x_sentence)  \n",
    "        x_sentence = self.sen_emb(x_sentence)\n",
    "        #print(\"x_sentence\",x_sentence)##\n",
    "        \n",
    "        pos1_input = tf.cast(inputs[1], tf.float32)\n",
    "        x_pos1 = self.pos1_f(pos1_input)\n",
    "        x_pos1 = self.pos1_emb(x_pos1)\n",
    "        #print(\"x_pos1\",x_pos1)##\n",
    "        \n",
    "         \n",
    "        pos2_input = tf.cast(inputs[2], tf.float32)\n",
    "        x_pos2 = self.pos2_f(pos2_input)\n",
    "        x_pos2 = self.pos2_emb(x_pos2)\n",
    "        #print(\"x_pos2\",x_pos2)##\n",
    "        \n",
    "        \n",
    "        e_input = tf.cast(inputs[3], tf.float32)\n",
    "        x_e = self.e_f(e_input)\n",
    "        x_e = self.e_emb(x_e)\n",
    "        #print(\"x_e\",x_e)##\n",
    "        \n",
    "        \n",
    "        x_feature = tf.concat([x_sentence,x_pos1,x_pos2,x_e], 1)\n",
    "        #print(\"x_feature:\",x_feature)##\n",
    "        x_feature = self.feature(x_feature)\n",
    "        #print(\"x_feature:\",x_feature)##\n",
    "        \n",
    "        output = self.preds(x_feature)\n",
    "        \n",
    "        return output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(2064, 20, 20)\n",
      "(2064, 20, 20)\n",
      "(2064, 20, 20)\n",
      "(2064, 20, 20)\n",
      "(2064, 6)\n"
     ]
    }
   ],
   "source": [
    "print(train.shape)\n",
    "print(position1.shape)\n",
    "print(position2.shape)\n",
    "print(E.shape)\n",
    "print(labels.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/50\n",
      "33/33 [==============================] - 44s 619ms/step - loss: 1.3943 - acc: 0.4169\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 2/50\n",
      "33/33 [==============================] - 19s 574ms/step - loss: 0.8812 - acc: 0.6560\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 3/50\n",
      "33/33 [==============================] - 19s 573ms/step - loss: 0.6800 - acc: 0.7477\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 4/50\n",
      "33/33 [==============================] - 19s 564ms/step - loss: 0.5333 - acc: 0.8063\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 5/50\n",
      "33/33 [==============================] - 19s 565ms/step - loss: 0.4467 - acc: 0.8483\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 6/50\n",
      "33/33 [==============================] - 18s 559ms/step - loss: 0.4161 - acc: 0.8359\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 7/50\n",
      "33/33 [==============================] - 19s 563ms/step - loss: 0.3527 - acc: 0.8749\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 8/50\n",
      "33/33 [==============================] - 19s 585ms/step - loss: 0.3369 - acc: 0.8679\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 9/50\n",
      "33/33 [==============================] - 19s 568ms/step - loss: 0.3176 - acc: 0.8719\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 10/50\n",
      "33/33 [==============================] - 19s 565ms/step - loss: 0.3325 - acc: 0.8645\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 11/50\n",
      "33/33 [==============================] - 19s 573ms/step - loss: 0.2929 - acc: 0.8832\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 12/50\n",
      "33/33 [==============================] - 19s 565ms/step - loss: 0.3085 - acc: 0.8777\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 13/50\n",
      "33/33 [==============================] - 19s 570ms/step - loss: 0.2843 - acc: 0.8827\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 14/50\n",
      "33/33 [==============================] - 20s 598ms/step - loss: 0.2635 - acc: 0.8929\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 15/50\n",
      "33/33 [==============================] - 19s 562ms/step - loss: 0.2465 - acc: 0.8954\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 16/50\n",
      "33/33 [==============================] - 19s 562ms/step - loss: 0.2730 - acc: 0.8798\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 17/50\n",
      "33/33 [==============================] - 19s 565ms/step - loss: 0.2379 - acc: 0.9018\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 18/50\n",
      "33/33 [==============================] - 19s 570ms/step - loss: 0.2235 - acc: 0.8948\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 19/50\n",
      "33/33 [==============================] - 18s 556ms/step - loss: 0.2264 - acc: 0.8972\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 20/50\n",
      "33/33 [==============================] - 19s 588ms/step - loss: 0.2336 - acc: 0.8963\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 21/50\n",
      "33/33 [==============================] - 19s 575ms/step - loss: 0.2090 - acc: 0.9056\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 22/50\n",
      "33/33 [==============================] - 19s 561ms/step - loss: 0.2207 - acc: 0.9145\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 23/50\n",
      "33/33 [==============================] - 19s 573ms/step - loss: 0.2405 - acc: 0.8965\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 24/50\n",
      "33/33 [==============================] - 20s 601ms/step - loss: 0.2322 - acc: 0.8852\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 25/50\n",
      "33/33 [==============================] - 19s 582ms/step - loss: 0.2258 - acc: 0.8957\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 26/50\n",
      "33/33 [==============================] - 19s 573ms/step - loss: 0.1994 - acc: 0.9029\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 27/50\n",
      "33/33 [==============================] - 19s 584ms/step - loss: 0.1912 - acc: 0.9098\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 28/50\n",
      "33/33 [==============================] - 19s 568ms/step - loss: 0.2025 - acc: 0.9077\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 29/50\n",
      "33/33 [==============================] - 19s 566ms/step - loss: 0.1974 - acc: 0.9056\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 30/50\n",
      "33/33 [==============================] - 19s 574ms/step - loss: 0.1893 - acc: 0.9059\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 31/50\n",
      "33/33 [==============================] - 19s 569ms/step - loss: 0.1890 - acc: 0.9068\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 32/50\n",
      "33/33 [==============================] - 19s 571ms/step - loss: 0.2001 - acc: 0.9129\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 33/50\n",
      "33/33 [==============================] - 19s 577ms/step - loss: 0.2091 - acc: 0.9035\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 34/50\n",
      "33/33 [==============================] - 19s 588ms/step - loss: 0.1767 - acc: 0.9153\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 35/50\n",
      "33/33 [==============================] - 19s 572ms/step - loss: 0.1778 - acc: 0.9159\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 36/50\n",
      "33/33 [==============================] - 19s 570ms/step - loss: 0.1819 - acc: 0.9176\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 37/50\n",
      "33/33 [==============================] - 19s 572ms/step - loss: 0.1922 - acc: 0.9116\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 38/50\n",
      "33/33 [==============================] - 20s 599ms/step - loss: 0.1775 - acc: 0.9178\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 39/50\n",
      "33/33 [==============================] - 19s 587ms/step - loss: 0.1890 - acc: 0.9037\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 40/50\n",
      "33/33 [==============================] - 19s 569ms/step - loss: 0.1802 - acc: 0.9160\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 41/50\n",
      "33/33 [==============================] - 19s 569ms/step - loss: 0.1778 - acc: 0.9137\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 42/50\n",
      "33/33 [==============================] - 19s 568ms/step - loss: 0.1976 - acc: 0.9018\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 43/50\n",
      "33/33 [==============================] - 19s 568ms/step - loss: 0.1735 - acc: 0.9179\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 44/50\n",
      "33/33 [==============================] - 19s 577ms/step - loss: 0.1830 - acc: 0.9101\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 45/50\n",
      "33/33 [==============================] - 19s 574ms/step - loss: 0.1776 - acc: 0.9121\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 46/50\n",
      "33/33 [==============================] - 19s 575ms/step - loss: 0.1928 - acc: 0.9004\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 47/50\n",
      "33/33 [==============================] - 19s 563ms/step - loss: 0.1740 - acc: 0.9207\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 48/50\n",
      "33/33 [==============================] - 19s 572ms/step - loss: 0.1504 - acc: 0.9270\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 49/50\n",
      "33/33 [==============================] - 19s 573ms/step - loss: 0.1712 - acc: 0.9030\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n",
      "Epoch 50/50\n",
      "33/33 [==============================] - 19s 568ms/step - loss: 0.1658 - acc: 0.9104\n",
      "WARNING:tensorflow:Can save best model only with val_acc available, skipping.\n"
     ]
    }
   ],
   "source": [
    "model = HAN()\n",
    "model.compile(loss='categorical_crossentropy',\n",
    "                      optimizer='adam',\n",
    "                      metrics=['acc'])\n",
    "\n",
    "cp = ModelCheckpoint('model.hdf5',monitor='val_acc',verbose=1,save_best_only=True)\n",
    "history = model.fit([train,position1,position2,E],labels,batch_size=64,epochs=50,callbacks=[cp])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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ERGJKCCIiAiggCAiIhEFBBERARQQREQkooAgIiKAAoKIiEQUEEREBKjDz0No3bq1d+3aNaVj161bR9OmTas3QXVEtuZd+c4uynfFpkyZstzd2yT7rM4GhK5duzJ58uSUji0uLqaoqKh6E1RHZGvele/sonxXzMzmVfSZqoxERARQQBARkYgCgoiIAHW4DUFE6qaSkhIWLlzIxo0ba+wazZs359NPP62x89dW8fnOz8+nY8eONGzYcIePV0AQkbRauHAhBQUFdO3alfD45+q3Zs0aCgoKauTctVks3+7OihUrWLhwId26ddvh41VlJCJptXHjRlq1alVjwUDAzGjVqlWVS2EKCCKSdgoGNS+V7zj7AsKMGXR7/HFYsSLTKRGRWsDdy62XlpZuXZ4+ffrW5Q0bNlTYLrF69WqmTJnCggULWLVqVY2kMx2yLyDMnk2X55+H+fMznRIRyZAZM2bwwgsvAHDZZZexYcMGpk6dCsAVV1wBwOLFi7nrrru2BowPPvhga4D4/PPPy51vzJgxrFu3jl133ZV7770XCIFl7ty5203HmjVreOihh6otXzsrLQHBzA40s5e383m+mZ1tZseZ2S1mVnPpahON2F62rMYuISK1W+/evWnQoAEQqlYaN27MmDFjAGjZsiUA48aN45577mHmzJkMGjSI8847j0cffZSioiLOPvtsVq9eDcDKlSv505/+RE5ODlOmTOGbb77hvffe4+2332bJkiXbTcfkyZNrVW+otPQycvcPzOz87exyFFDq7q+aWWegL/BxjSSmbdvw/vXXNXJ6EakbDjvsMAByc8NtsHHjxls/W7p0Kbm5ubRs2ZIHHniAf/7zn/zqV7/i8ssvJz8/n7y8PCCUAl599VUKCgq48cYbcXfmzZvHjBkzmD9/PhMmTNhuGoYNG8akSZNqKIdVV1u6nRYDraLlDsCXyXaKgsr5AO3ataO4uLjKF2qwdi2HALM/+ICFHTumkNS6be3atSl9b3Wd8l17NG/enDVr1gDQ6JpryPnkk2o9f1mfPpTedtvWayR66623uOeee+jUqRNPPfUUZWVlrFmzhpKSEtasWcOmTZuYMWMGr732Gq+//jq33norgwcPZsuWLbzyyiv89Kc/5Yc//CEAX375JSeccAKTJ0/mnHPOoaysjNGjR3PhhReyaNGicnmtyKZNm8rt8/TTT9OtWze++OILRo4cSaNGjXj55Zdp2rQpL7zwAjfffDPdunVLuq20tLTcuTZu3Filv3+tCAjuvgpYZWY9gdnuvrKC/UYDowEGDBjgKU1e5U5Zbi49mzenpya/yhrKd+3x6aefbhsjkJcHUdVNtcnLY1ODBhWOQzj11FM59dRTufHGGykoKCA3N5eCggIaNWq09X348OHstttuNGnShI4dO7J582ZatGjBqlWrePTRR3n88cd57rnn6NOnDwDz5s3jsssu21pC+Oyzz9i0aRNHH300N91003aTG7suwIQJE+jRowfHHHMMn376Ka+88goXXHABkydP5s477+TII49kw4YNFBQUJN2WOP4iPz+ffv367fBXVysCAoCZtQf6ufvTNXwhSlq0oJGqjEQy74EHaua8lfwqjyktLd3alhDf2+jDDz/k7rvvZuTIkWzatIm3336bxo0b89FHH1FaWsrw4cPLneess87ipJNO4uOPP2bRokUcd9xxAMyaNYuSkpIdHi08depUTjzxRAB69OjBww8/DMDFF1/MJZdcQocOHbj55psr3Laz0t7LyMwamlmXhG15wDHu/lL0eWFNpmFzy5ZqQxDJcqtXr2bSpEnssssuAJSVlW39bNCgQTz55JMMGDCA1atXM2bMGP7yl78AsHz5cu6++242bdrESy+9xJAhQ3jwwQcZOnQoBx10EBdddBFFRUUUFRVxwQUXVKmNoHfv3syPekDOnz+ffffdF3dn48aNPPnkkxxwwAG89dZbSbdVh3T1MjoUOMTMTgD2Bx5O2OVcYISZPQe8A2ypyfSUNG+uXkYiWWz16tV88sknLFy4kLPOOgvYVkLYsmULU6ZM4eqrr2b69Ok0b96cRo0acdppp1FSUkL79u3Zf//9uf/++zn11FP58MMPGT9+PIcffjijR4/mzjvvZNSoUUyYMIHi4mIOP/zwCtNRXFzM9OnTt9bzH3300cyZM4e//vWvvPPOO5xzzjmYGVdeeSWvvfYaK1eupG/fvkm3VQt3r5Ov/v37e6qWHHGEe7duKR9fl02cODHTScgI5bv2mDlzZo1fY/Xq1ZXuM23aNH///fe3rpeUlLi7+9VXX+0lJSVeWlrqJSUl/te//tXd3f/xj3/40KFDfcmSJe7uvnjxYnd3/+ijj/zpp5/2NWvW+AcffODjxo3zZcuW+Q033ODXXXedz5gx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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.rcParams['font.sans-serif']=['simsun'] #用来正常显示中文标签\n",
    "plt.rcParams['axes.unicode_minus']=False #用来正常显示负号\n",
    "\n",
    "fig, ax = plt.subplots()#创建一个figure \n",
    "ax.plot(history.history['loss'], '-r', label='训练集 loss')\n",
    "\n",
    "####打开网格\n",
    "ax.grid(True)\n",
    "\n",
    "####定义x, y轴的名称\n",
    "ax.set_xlabel('epoch',fontsize=15)\n",
    "ax.set_ylabel('loss',fontsize=15)\n",
    "\n",
    "####定义标题\n",
    "fig.suptitle('HAN模型损失函数变化曲线',fontsize=15)\n",
    "\n",
    "####展示图例 legend loc=是用来定义图例的位置的，还有很多选择，大家可以自己尝试\n",
    "ax.legend(loc = 'upper right')\n",
    "\n",
    "plt.savefig(\"HAN模型损失函数变化曲线.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "pred_test = model.predict([test,position1_t,position2_t,E_t])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "result_ls = []\n",
    "for i in pred_test:\n",
    "    res = max(list(i))\n",
    "    idx = list(i).index(res)\n",
    "    result_ls.append(idx)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0     0.8586    0.9291    0.8925       268\n",
      "           1     0.8472    0.8971    0.8714        68\n",
      "           2     0.7778    0.8485    0.8116        33\n",
      "           3     0.8889    0.8000    0.8421        50\n",
      "           4     0.9348    0.8600    0.8958        50\n",
      "           5     0.8945    0.8128    0.8517       219\n",
      "\n",
      "    accuracy                         0.8706       688\n",
      "   macro avg     0.8670    0.8579    0.8609       688\n",
      "weighted avg     0.8728    0.8706    0.8701       688\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import classification_report\n",
    "print(classification_report(labels_t,result_ls,digits = 4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
